/**
 * 文件名称: display.c
 * 摘    要: 画图软件显示部分代码
 * 来    源: huenrong
 *
 * 当前版本: 1.0 
 * 作    者: huenrong
 * 完成日期: 2019-07-28
 **/

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <math.h>

#include "display.h"
#include "../touch/touch.h"
#include "./framebuffer/framebuffer.h"
#include "./framebuffer_png/framebuffer_png.h"


extern struct touch_info g_touch_info;      // 记录当前未处理的触摸信息
struct display_info g_display_info = {0};   // 记录当前显示信息

/******************************************************************************
函数名称: framebuffer_display_operating_area
函数功能: framebuffer显示操作区域
函数参数: 无
函数返回: 无
******************************************************************************/
void framebuffer_display_operating_area(void)
{
    framebuffer_draw_background(SCREEN_WIDTH, SCREEN_HEIGHT, WHITE);        // 让整块屏幕显示白色背景

    // 显示左半部分颜色选择区域
    display_png_picture(RED_PEN_PATH, RED_PEN_AREA_X_START, RED_PEN_AREA_Y_START);          // 显示红色笔
    display_png_picture(GREEN_PEN_PATH, GREEN_PEN_AREA_X_START, GREEN_PEN_AREA_Y_START);    // 显示绿色笔
    display_png_picture(BLUE_PEN_PATH, BLUE_PEN_AREA_X_START, BLUE_PEN_AREA_Y_START);       // 显示蓝色笔
    display_png_picture(BLACK_PEN_PATH, BLACK_PEN_AREA_X_START, BLACK_PEN_AREA_Y_START);    // 显示黑色笔

    // 显示有效绘制区域左边界
    framebuffer_draw_area(SCREEN_DRAW_X_START-1, SCREEN_DRAW_X_START-1, SCREEN_DRAW_Y_START, SCREEN_DRAW_Y_END, BLACK);

    // 显示有效绘制区域右边界
    framebuffer_draw_area(SCREEN_DRAW_X_END+1, SCREEN_DRAW_X_END+1, SCREEN_DRAW_Y_START, SCREEN_DRAW_Y_END, BLACK);

    // 显示右半部分功能操作区域
    display_png_picture(THIN_LINE_PATH, THIN_LINE_AREA_X_START, THIN_LINE_AREA_Y_START);    // 显示细线
    display_png_picture(THICK_LINE_PATH, THICK_LINE_AREA_X_START, THICK_LINE_AREA_Y_START); // 显示粗线
    display_png_picture(ERASER_PATH, ERASER_AREA_X_START, ERASER_AREA_Y_START);             // 显示橡皮擦
    display_png_picture(RESET_PATH, RESET_AREA_X_START, RESET_AREA_Y_START);                // 显示复位
}

/******************************************************************************
函数名称: update_selected_color
函数功能: 更新当前选中颜色, 更新对应图标(如果选中某一个颜色, 
          则将该颜色对应的图标画方框, 同时需要将其他颜色显示成原始图片)
函数参数: 无
函数返回: 无
******************************************************************************/
void update_selected_color(void)
{
    switch (g_display_info.draw_color)
    {
        case RED:
        {
            framebuffer_draw_area(RED_PEN_AREA_X_START, (RED_PEN_AREA_X_END - 1), 
                                  RED_PEN_AREA_Y_START, (RED_PEN_AREA_Y_END - 1), RED);
            
            display_png_picture(GREEN_PEN_PATH, GREEN_PEN_AREA_X_START, GREEN_PEN_AREA_Y_START);    // 显示绿色笔
            display_png_picture(BLUE_PEN_PATH, BLUE_PEN_AREA_X_START, BLUE_PEN_AREA_Y_START);       // 显示蓝色笔
            display_png_picture(BLACK_PEN_PATH, BLACK_PEN_AREA_X_START, BLACK_PEN_AREA_Y_START);    // 显示黑色笔

            break;
        }

        case GREEN:
        {
            framebuffer_draw_area(GREEN_PEN_AREA_X_START, (GREEN_PEN_AREA_X_END - 1), 
                                  GREEN_PEN_AREA_Y_START, (GREEN_PEN_AREA_Y_END - 1), GREEN);
            
            display_png_picture(RED_PEN_PATH, RED_PEN_AREA_X_START, RED_PEN_AREA_Y_START);          // 显示红色笔
            display_png_picture(BLUE_PEN_PATH, BLUE_PEN_AREA_X_START, BLUE_PEN_AREA_Y_START);       // 显示蓝色笔
            display_png_picture(BLACK_PEN_PATH, BLACK_PEN_AREA_X_START, BLACK_PEN_AREA_Y_START);    // 显示黑色笔

            break;
        }

        case BLUE:
        {
            framebuffer_draw_area(BLUE_PEN_AREA_X_START, (BLUE_PEN_AREA_X_END - 1), 
                                  BLUE_PEN_AREA_Y_START, (BLUE_PEN_AREA_Y_END - 1), BLUE);

            display_png_picture(RED_PEN_PATH, RED_PEN_AREA_X_START, RED_PEN_AREA_Y_START);          // 显示红色笔
            display_png_picture(GREEN_PEN_PATH, GREEN_PEN_AREA_X_START, GREEN_PEN_AREA_Y_START);    // 显示绿色笔
            display_png_picture(BLACK_PEN_PATH, BLACK_PEN_AREA_X_START, BLACK_PEN_AREA_Y_START);    // 显示黑色笔

            break;
        }

        case BLACK:
        {
            framebuffer_draw_area(BLACK_PEN_AREA_X_START, (BLACK_PEN_AREA_X_END - 1), 
                                  BLACK_PEN_AREA_Y_START, (BLACK_PEN_AREA_Y_END - 1), BLACK);
            
            display_png_picture(RED_PEN_PATH, RED_PEN_AREA_X_START, RED_PEN_AREA_Y_START);          // 显示红色笔
            display_png_picture(GREEN_PEN_PATH, GREEN_PEN_AREA_X_START, GREEN_PEN_AREA_Y_START);    // 显示绿色笔
            display_png_picture(BLUE_PEN_PATH, BLUE_PEN_AREA_X_START, BLUE_PEN_AREA_Y_START);       // 显示蓝色笔

            break;
        }
        
        default:
            break;
    }
}

/******************************************************************************
函数名称: update_selected_line
函数功能: 更新当前选中画笔粗细, 更新对应图标(如果选中某一种粗细的画笔, 
          则将对应粗细的画笔图标用黑色方框框选, 同时将其它粗细的画笔显示成原始图片)
函数参数: 无
函数返回: 无
******************************************************************************/
void update_selected_line(void)
{
    switch (g_display_info.brush_thickness)
    {
        case THIN_LINE:
        {
            framebuffer_draw_line(THIN_LINE_AREA_X_START, THIN_LINE_AREA_Y_START, 
                                  (THIN_LINE_AREA_X_END - 1), THIN_LINE_AREA_Y_START, BLACK);
            framebuffer_draw_line((THIN_LINE_AREA_X_END - 1), THIN_LINE_AREA_Y_START, 
                                  (THIN_LINE_AREA_X_END - 1), (THIN_LINE_AREA_Y_END - 1), BLACK);
            framebuffer_draw_line((THIN_LINE_AREA_X_END - 1), (THIN_LINE_AREA_Y_END - 1), 
                                  THIN_LINE_AREA_X_START, (THIN_LINE_AREA_Y_END - 1), BLACK);
            framebuffer_draw_line(THIN_LINE_AREA_X_START, (THIN_LINE_AREA_Y_END - 1), 
                                  THIN_LINE_AREA_X_START, THIN_LINE_AREA_Y_START, BLACK);
            
            
            display_png_picture(THICK_LINE_PATH, THICK_LINE_AREA_X_START, THICK_LINE_AREA_Y_START); // 显示粗线

            break;
        }

        case THICK_LINE:
        {
            framebuffer_draw_line(THICK_LINE_AREA_X_START, THICK_LINE_AREA_Y_START, 
                                  (THICK_LINE_AREA_X_END - 1), THICK_LINE_AREA_Y_START, BLACK);
            framebuffer_draw_line((THICK_LINE_AREA_X_END - 1), THICK_LINE_AREA_Y_START, 
                                  (THICK_LINE_AREA_X_END - 1), (THICK_LINE_AREA_Y_END - 1), BLACK);
            framebuffer_draw_line((THICK_LINE_AREA_X_END - 1), (THICK_LINE_AREA_Y_END - 1), 
                                  THICK_LINE_AREA_X_START, (THICK_LINE_AREA_Y_END - 1), BLACK);
            framebuffer_draw_line(THICK_LINE_AREA_X_START, (THICK_LINE_AREA_Y_END - 1), 
                                  THICK_LINE_AREA_X_START, THICK_LINE_AREA_Y_START, BLACK);

            display_png_picture(THIN_LINE_PATH, THIN_LINE_AREA_X_START, THIN_LINE_AREA_Y_START);    // 显示细线

            break;
        }
        
        default:
            break;
    }
}

/******************************************************************************
函数名称: update_eraser_area
函数功能: 更新擦除区域图标
函数参数: 无
函数返回: 无
******************************************************************************/
void update_eraser_area(void)
{
    if (1 == g_display_info.eraser_flag)
    {
        framebuffer_draw_line(ERASER_AREA_X_START, ERASER_AREA_Y_START, 
                              (ERASER_AREA_X_END - 1), ERASER_AREA_Y_START, BLACK);
        framebuffer_draw_line((ERASER_AREA_X_END - 1), ERASER_AREA_Y_START, 
                              (ERASER_AREA_X_END - 1), (ERASER_AREA_Y_END - 1), BLACK);
        framebuffer_draw_line((ERASER_AREA_X_END - 1), (ERASER_AREA_Y_END - 1), 
                              ERASER_AREA_X_START, (ERASER_AREA_Y_END - 1), BLACK);
        framebuffer_draw_line(ERASER_AREA_X_START, (ERASER_AREA_Y_END - 1), 
                              ERASER_AREA_X_START, ERASER_AREA_Y_START, BLACK);
    }
    else
    {
        display_png_picture(ERASER_PATH, ERASER_AREA_X_START, ERASER_AREA_Y_START);             // 显示橡皮擦
    }
    
}

/******************************************************************************
函数名称: get_distance
函数功能: 获取两点之间的距离
函数参数: start_x: 起始点x坐标
          start_y: 起始点y坐标
          end_x: 结束点x坐标
          end_y: 结束点y坐标
函数返回: 成功: 返回两点之间的距离
          失败: 返回-1
******************************************************************************/
static int get_distance(const int start_x, const int start_y, const int end_x, const int end_y)
{
    // 求两点间距离的公式: sqrt((x1 - x2)^2 + (y1 - y2)^2)
    int distance = -1;

    distance = sqrt((start_x - end_x) * (start_x - end_x) + (start_y - end_y) * (start_y - end_y));

    return distance;
}

/******************************************************************************
函数名称: display_thread
函数功能: 显示处理线程
函数参数: 无
函数返回: 无
******************************************************************************/
static void display_thread(void)
{
    int distance = -1;

    while (1)
    {
        // 队列头和尾的位置不相等, 说明有数据需要处理
        if (g_touch_info.header != g_touch_info.tail)
        {
            // 每次取数据时判断队列上一个位置的值是否是-1

            // 如果当前从队列0位置取数据, 则判断TOUCH_INFO_MAX_LENGTH-1位置
            if (0 == g_touch_info.header)
            {
                if ((-1 == g_touch_info.position[TOUCH_INFO_MAX_LENGTH - 1].x) && 
                    (-1 == g_touch_info.position[TOUCH_INFO_MAX_LENGTH - 1].y))     // -1说明刚开始触摸
                {
                    // 触摸第一个点, 只显示点
                    // framebuffer_draw_point(g_touch_info.position[g_touch_info.header].x, 
                    //                g_touch_info.position[g_touch_info.header].y, 
                    //                g_display_info.draw_color);

                    if (THICK_LINE == g_display_info.brush_thickness)       // 画粗线
                    {
                        framebuffer_draw_area(g_touch_info.position[g_touch_info.header].x - 2, 
                                      g_touch_info.position[g_touch_info.header].x + 2, 
                                      g_touch_info.position[g_touch_info.header].y - 2, 
                                      g_touch_info.position[g_touch_info.header].y + 2, 
                                      g_display_info.draw_color);
                    }
                    else // 画细线
                    {
                        framebuffer_draw_area(g_touch_info.position[g_touch_info.header].x - 1, 
                                              g_touch_info.position[g_touch_info.header].x + 1, 
                                              g_touch_info.position[g_touch_info.header].y - 1, 
                                              g_touch_info.position[g_touch_info.header].y + 1, 
                                              g_display_info.draw_color);
                    }
                    
                    // 队列头向后移动
                    g_touch_info.header++;
                    if (g_touch_info.header >= TOUCH_INFO_MAX_LENGTH)
                    {
                        g_touch_info.header = 0;
                    }
                    
                    continue;
                }
            }
            
            // 不是刚开始触摸, 则判断两点之间的距离是否足够小
            if (0 == g_touch_info.header)
            {
                distance = get_distance(g_touch_info.position[TOUCH_INFO_MAX_LENGTH - 1].x, 
                                        g_touch_info.position[TOUCH_INFO_MAX_LENGTH - 1].y, 
                                        g_touch_info.position[g_touch_info.header].x, 
                                        g_touch_info.position[g_touch_info.header].y);
            }
            else
            {
                distance = get_distance(g_touch_info.position[g_touch_info.header - 1].x, 
                                        g_touch_info.position[g_touch_info.header - 1].y, 
                                        g_touch_info.position[g_touch_info.header].x, 
                                        g_touch_info.position[g_touch_info.header].y);
            }
            if (distance > 20)       // 如果两点之间的距离大于5, 则重新开始画点
            {
                if (THICK_LINE == g_display_info.brush_thickness)       // 画粗线
                {
                    framebuffer_draw_area(g_touch_info.position[g_touch_info.header].x - 2, 
                                          g_touch_info.position[g_touch_info.header].x + 2, 
                                          g_touch_info.position[g_touch_info.header].y - 2, 
                                          g_touch_info.position[g_touch_info.header].y + 2, 
                                          g_display_info.draw_color);
                }
                else // 画细线
                {
                    framebuffer_draw_area(g_touch_info.position[g_touch_info.header].x - 1, 
                                          g_touch_info.position[g_touch_info.header].x + 1, 
                                          g_touch_info.position[g_touch_info.header].y - 1, 
                                          g_touch_info.position[g_touch_info.header].y + 1, 
                                          g_display_info.draw_color);
                }
            }
            else    // 两点之间的距离不超过5, 则继续划线
            {
                if (0 == g_touch_info.header)
                {
                    if (THICK_LINE == g_display_info.brush_thickness)       // 画粗线
                    {
                        framebuffer_draw_wide_line(g_touch_info.position[g_touch_info.header - 1].x, 
                                                   g_touch_info.position[g_touch_info.header - 1].y, 
                                                   g_touch_info.position[g_touch_info.header].x, 
                                                   g_touch_info.position[g_touch_info.header].y, 
                                                   g_display_info.draw_color, 2);
                    }
                    else // 画细线
                    {
                        framebuffer_draw_wide_line(g_touch_info.position[g_touch_info.header - 1].x, 
                                                   g_touch_info.position[g_touch_info.header - 1].y, 
                                                   g_touch_info.position[g_touch_info.header].x, 
                                                   g_touch_info.position[g_touch_info.header].y, 
                                                   g_display_info.draw_color, 1);
                    }
                }
                else
                {
                    if (THICK_LINE == g_display_info.brush_thickness)       // 画粗线
                    {
                        framebuffer_draw_wide_line(g_touch_info.position[g_touch_info.header - 1].x, 
                                                   g_touch_info.position[g_touch_info.header - 1].y, 
                                                   g_touch_info.position[g_touch_info.header].x, 
                                                   g_touch_info.position[g_touch_info.header].y, 
                                                   g_display_info.draw_color, 2);
                    }
                    else // 画细线
                    {
                        framebuffer_draw_wide_line(g_touch_info.position[g_touch_info.header - 1].x, 
                                                   g_touch_info.position[g_touch_info.header - 1].y, 
                                                   g_touch_info.position[g_touch_info.header].x, 
                                                   g_touch_info.position[g_touch_info.header].y, 
                                                   g_display_info.draw_color, 1);
                    }
                }
            }

            // 将数据队列中上上个位置赋值为-1
            if (1 == g_touch_info.header)
            {
                g_touch_info.position[TOUCH_INFO_MAX_LENGTH - 1].x = -1;
                g_touch_info.position[TOUCH_INFO_MAX_LENGTH - 1].y = -1;
            }
            else
            {
                g_touch_info.position[g_touch_info.header - 2].x = -1;
                g_touch_info.position[g_touch_info.header - 2].y = -1;
            }

            // 队列头向后移动
            g_touch_info.header++;
            if (g_touch_info.header >= TOUCH_INFO_MAX_LENGTH)
            {
                g_touch_info.header = 0;
            }
        }
    }
}

/******************************************************************************
函数名称: creat_display_thread
函数功能: 创建显示处理线程
函数参数: 无
函数返回: 成功: 返回0
          失败: 返回-1
******************************************************************************/
int creat_display_thread(void)
{
    int ret = -1;
    unsigned char fail_num = 0;     // 创建线程失败次数
    pthread_t display_thread_id = -1;       // 显示处理线程ID

    while (1)
    {
        // 创建显示处理线程
        ret = pthread_create(&display_thread_id, NULL, (void *)display_thread, NULL);
        if (0 != ret)
        {
            printf("creat_display_thread pthread_create fail!\n");
            usleep(50 * 1000);
            fail_num++;
            if (3 == fail_num)      // 创建3次都失败则认为失败
            {
                return -1;
            }
        }
        else if (0 == ret)      // 线程创建成功，分离线程，并退出
        {
            pthread_detach(display_thread_id);       // 线程分离，结束时自动回收资源

            return 0;
        }
    }
}
